home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / mops / tinyclos.scm < prev    next >
Text File  |  1993-07-23  |  27KB  |  861 lines

  1. ; Mode: Scheme
  2. ;
  3. ;
  4. ; **********************************************************************
  5. ; Copyright (c) 1992 Xerox Corporation.  
  6. ; All Rights Reserved.  
  7. ;
  8. ; Use, reproduction, and preparation of derivative works are permitted.
  9. ; Any copy of this software or of any derivative work must include the
  10. ; above copyright notice of Xerox Corporation, this paragraph and the
  11. ; one after it.  Any distribution of this software or derivative works
  12. ; must comply with all applicable United States export control laws.
  13. ;
  14. ; This software is made available AS IS, and XEROX CORPORATION DISCLAIMS
  15. ; ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
  16. ; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  17. ; PURPOSE, AND NOTWITHSTANDING ANY OTHER PROVISION CONTAINED HEREIN, ANY
  18. ; LIABILITY FOR DAMAGES RESULTING FROM THE SOFTWARE OR ITS USE IS
  19. ; EXPRESSLY DISCLAIMED, WHETHER ARISING IN CONTRACT, TORT (INCLUDING
  20. ; NEGLIGENCE) OR STRICT LIABILITY, EVEN IF XEROX CORPORATION IS ADVISED
  21. ; OF THE POSSIBILITY OF SUCH DAMAGES.
  22. ; **********************************************************************
  23. ;
  24. ; EDIT HISTORY:
  25. ;
  26. ;      10/**/92  Gregor  Originally Written
  27. ; 1.0  11/10/92  Gregor  Changed names of generic invocation generics.
  28. ;                        Changed compute-getters-and-setters protocol.
  29. ;                        Made comments match the code.
  30. ;                        Changed maximum line width to 72.
  31. ; 1.1  11/24/92  Gregor  Fixed bug in compute-method-more-specific?,
  32. ;                        wrt the use of for-each.
  33. ;                        Both methods on allocate instance failed to
  34. ;                        initialize fields properly.
  35. ;                        The specializers and procedure initargs are
  36. ;                        now required when creating a method, that is,
  37. ;                        they no longer default.  No working program
  38. ;                        should notice this change.
  39. ; 1.2  12/02/92  Gregor  Fix minor things that improve portability:
  40. ;                         - DEFINE needs 2 args in R4Rs
  41. ;                         - Conditionalize printer hooks.
  42. ;                         - () doesn't evaluate to ()
  43. ;
  44. ; 1.3  12/08/92  Gregor  More minor things:
  45. ;                         - () really doesn't evaluate to () damnit!
  46. ;                         - It turns out DEFINE-MACRO is never used.
  47. ;                         - Confusion over the "failure" return value
  48. ;                           of ASSQ -- ASSQ returns #f if the key is
  49. ;                           not found.
  50. ;                         - SEQUENCE   --> BEGIN
  51. ;                         - LAST-PAIR  --> last now in support
  52. ;                        Change instance rep to protect Schemes that
  53. ;                        don't detect circular structures when
  54. ;                        printing.
  55. ;                        A more reasonable error message when there
  56. ;                        are no applicable methods or next methods.
  57. ;
  58. ;
  59. ;       
  60. (define tiny-clos-version "1.3")
  61.  
  62. '(;Stuff to make emacs more reasonable.
  63.  
  64.   (put 'letrec 'lisp-indent-hook 1)
  65.  
  66.   (put 'make-method  'lisp-indent-hook 1)
  67.   (put 'add-method   'lisp-indent-hook 'defun)
  68.  
  69.  )
  70. ;
  71. ; A very simple CLOS-like language, embedded in Scheme, with a simple
  72. ; MOP.  The features of the default base language are:
  73. ;
  74. ;   * Classes, with instance slots, but no slot options.
  75. ;   * Multiple-inheritance.
  76. ;   * Generic functions with multi-methods and class specializers only.
  77. ;   * Primary methods and call-next-method; no other method combination.
  78. ;   * Uses Scheme's lexical scoping facilities as the class and generic
  79. ;     function naming mechanism.  Another way of saying this is that
  80. ;     class, generic function and methods are first-class (meta)objects.
  81. ;
  82. ; While the MOP is simple, it is essentially equal in power to both MOPs
  83. ; in AMOP.  This implementation is not at all optimized, but the MOP is
  84. ; designed so that it can be optimized.  In fact, this MOP allows better
  85. ; optimization of slot access extenstions than those in AMOP.
  86. ;
  87. ;
  88. ; In addition to calling a generic, the entry points to the default base
  89. ; language are:
  90. ;
  91. ;   (MAKE-CLASS list-of-superclasses list-of-slot-names)
  92. ;   (MAKE-GENERIC)
  93. ;   (MAKE-METHOD list-of-specializers procedure)
  94. ;   (ADD-METHOD generic method)
  95. ;
  96. ;   (MAKE class . initargs)
  97. ;   (INITIALIZE instance initargs)            ;Add methods to this,
  98. ;                                             ;don't call it directly.
  99. ;   
  100. ;   (SLOT-REF  object slot-name)
  101. ;   (SLOT-SET! object slot-name new-value)
  102. ;
  103. ;
  104. ; So, for example, one might do:
  105. ;
  106. ;   (define <position> (make-class (list <object>) (list 'x 'y)))
  107. ;   (add-method initialize
  108. ;       (make-method (list <position>)
  109. ;         (lambda (call-next-method pos initargs)
  110. ;           (for-each (lambda (initarg-name slot-name)
  111. ;                       (slot-set! pos
  112. ;                                  slot-name
  113. ;                                  (getl initargs initarg-name 0)))
  114. ;                     '(x y)
  115. ;                     '(x y)))))
  116. ;
  117. ;   (set! p1 (make <position> 'x 1 'y 3))
  118. ;
  119. ;
  120. ;
  121. ; NOTE!  Do not use EQUAL? to compare objects!  Use EQ? or some hand
  122. ;        written procedure.  Objects have a pointer to their class,
  123. ;        and classes are circular structures, and ...
  124. ;
  125. ;
  126. ;
  127. ; The introspective part of the MOP looks like the following.  Note that
  128. ; these are ordinary procedures, not generics.
  129. ;
  130. ;   CLASS-OF
  131. ;
  132. ;   CLASS-DIRECT-SUPERS
  133. ;   CLASS-DIRECT-SLOTS
  134. ;   CLASS-CPL
  135. ;   CLASS-SLOTS
  136. ;
  137. ;   GENERIC-METHODS
  138. ;
  139. ;   METHOD-SPECIALIZERS
  140. ;   METHOD-PROCEDURE
  141. ;
  142. ;
  143. ; The intercessory protocol looks like (generics in uppercase):
  144. ;
  145. ;   make                        
  146. ;     ALLOCATE-INSTANCE
  147. ;     INITIALIZE                   (really a base-level generic)
  148. ;
  149. ;   class initialization
  150. ;     COMPUTE-CPL
  151. ;     COMPUTE-SLOTS
  152. ;     COMPUTE-GETTER-AND-SETTER
  153. ;
  154. ;   add-method                     (Notice this is not a generic!)
  155. ;     COMPUTE-APPLY-GENERIC
  156. ;       COMPUTE-METHODS
  157. ;         COMPUTE-METHOD-MORE-SPECIFIC?
  158. ;       COMPUTE-APPLY-METHODS
  159. ;
  160.  
  161. ;
  162. ; OK, now let's get going.  But, as usual, before we can do anything
  163. ; interesting, we have to muck around for a bit first.  First, we need  
  164. ; to load the support library.
  165. ;
  166. ; Note that there is no extension on the filename in the following load,
  167. ; in particular, it isn't "support.scm" even though that is the name of
  168. ; the file in the distribution at PARC.  The idea is that when people
  169. ; install the code at their site, they should rename all the files to
  170. ; the appropriate extension, and then not change the load.  This should
  171. ; also make things work with binary files and the like.  This comes from
  172. ; my understanding of the CL world...  I hope it is right.
  173. ;
  174. ;
  175. (load "support")
  176.  
  177. ;
  178. ; Then, we need to build what, in a more real implementation, would be
  179. ; the interface to the memory subsystem: instances and entities.  The
  180. ; former are used for instances of instances of <class>; the latter
  181. ; are used for instances of instances of <entity-class>.  In this MOP,
  182. ; none of this is visible to base- or MOP-level programmers.
  183. ;
  184. ; A few things to note, that have influenced the way all this is done:
  185. ;  
  186. ;   - R4RS doesn't provide a mechanism for specializing the
  187. ;     behavior of the printer for certain objects.
  188. ;     
  189. ;   - Some Scheme implementations bomb when printing circular
  190. ;     structures -- that is, arrays and/or lists that somehow
  191. ;     point back to themselves.
  192. ;
  193. ; So, the natural implementation of instances -- vectors whose first
  194. ; field point to the class -- is straight on out.  Instead, we use a
  195. ; procedure to `encapsulate' that natural representation.
  196. ;
  197. ; Having gone that far, it makes things simpler to unify the way normal
  198. ; instances and entities are handled, at least in the lower levels of
  199. ; the system.  Don't get faked out by this -- the user shouldn't think
  200. ; of normal instances as being procedures, they aren't. (At least not
  201. ; in this language.)  If you are using this to teach, you probably want
  202. ; to hide the implementation of instances and entities from people.
  203. ;
  204. ;
  205. (define %allocate-instance
  206.     (lambda (class nfields)
  207.       (%allocate-instance-internal
  208.        class
  209.        #t
  210.        (lambda args
  211.      (error "An instance isn't a procedure -- can't apply it."))
  212.        nfields)))
  213.  
  214. (define %allocate-entity
  215.     (lambda (class nfields)
  216.       (%allocate-instance-internal
  217.        class
  218.        #f
  219.        (lambda args
  220.      (error "Tried to call an entity before its proc is set."))
  221.        nfields)))
  222.  
  223. (define %allocate-instance-internal ???)
  224. (define %instance?                  ???)
  225. (define %instance-class             ???)
  226. (define %set-instance-class-to-self ???)   ;This is used only once
  227.                                            ;as part of bootstrapping
  228.                                            ;the braid.
  229. (define %set-instance-proc!  ???)
  230. (define %instance-ref        ???)
  231. (define %instance-set!       ???)
  232.  
  233. (letrec ((instances '())
  234.      (get-vector
  235.       (lambda (closure)
  236.         (let ((cell (assq closure instances)))
  237.           (if cell (cdr cell) #f)))))
  238.  
  239.   (set! %allocate-instance-internal
  240.     (lambda (class lock proc nfields)
  241.       (letrec ((vector (make-vector (+ nfields 3) #f))
  242.            (closure (lambda args
  243.                   (apply (vector-ref vector 0) args))))
  244.         (vector-set! vector 0 proc)
  245.         (vector-set! vector 1 lock)
  246.         (vector-set! vector 2 class)
  247.         (set! instances (cons (cons closure vector) instances))
  248.         closure)))
  249.            
  250.   (set! %instance?
  251.         (lambda (x) (not (null? (get-vector x)))))
  252.  
  253.   (set! %instance-class
  254.     (lambda (closure)
  255.       (let ((vector (get-vector closure)))
  256.         (vector-ref vector 2))))
  257.  
  258.   (set! %set-instance-class-to-self
  259.     (lambda (closure)
  260.       (let ((vector (get-vector closure)))
  261.         (vector-set! vector 2 closure))))
  262.            
  263.   (set! %set-instance-proc!
  264.         (lambda (closure proc)
  265.       (let ((vector (get-vector closure)))
  266.         (if (vector-ref vector 1)
  267.         (error "Can't set procedure of instance.")
  268.         (vector-set! vector 0 proc)))))
  269.     
  270.   (set! %instance-ref
  271.         (lambda (closure index)
  272.       (let ((vector (get-vector closure)))
  273.         (vector-ref vector (+ index 3)))))
  274.           
  275.   (set! %instance-set!
  276.         (lambda (closure index new-value)
  277.       (let ((vector (get-vector closure)))
  278.         (vector-set! vector (+ index 3) new-value))))
  279.   )
  280.  
  281.  
  282. ;
  283. ; %allocate-instance, %allocate-entity, %instance-ref, %instance-set!
  284. ; and class-of are the normal interface, from the rest of the code, to
  285. ; the low-level memory system.  One thing to take note of is that the
  286. ; protocol does not allow the user to add low-level instance
  287. ; representations.  I have never seen a way to make that work.
  288. ;
  289. ; Note that this implementation of class-of assumes the name of a the
  290. ; primitive classes that are set up later.
  291. (define class-of
  292.     (lambda (x)
  293.       (cond ((%instance? x)  (%instance-class x))
  294.  
  295.         ((boolean? x)    <boolean>)
  296.         ((symbol? x)     <symbol>)
  297.         ((char? x)       <char>)
  298.         ((vector? x)     <vector>)
  299.         ((pair? x)       <pair>)
  300.         ((number? x)     <number>)
  301.         ((string? x)     <string>)
  302.         ((procedure? x)  <procedure>))))
  303.  
  304.  
  305. ;
  306. ; Now we can get down to business.  First, we initialize the braid.
  307. ;
  308. ; For Bootstrapping, we define an early version of MAKE.  It will be
  309. ; changed to the real version later on.  String search for ``set! make''.
  310. ;
  311.  
  312. (define make
  313.     (lambda (class . initargs)
  314.       (cond ((or (eq? class <class>)
  315.          (eq? class <entity-class>))
  316.          (let* ((new (%allocate-instance
  317.               class
  318.               (length the-slots-of-a-class)))
  319.             (dsupers (getl initargs 'direct-supers '()))
  320.             (dslots  (map list
  321.                   (getl initargs 'direct-slots  '())))
  322.             (cpl     (let loop ((sups dsupers)
  323.                     (so-far (list new)))
  324.                   (if (null? sups)
  325.                       (reverse so-far)
  326.                       (loop (class-direct-supers
  327.                          (car sups))
  328.                         (cons (car sups)
  329.                           so-far)))))
  330.             (slots (apply append
  331.                   (cons dslots
  332.                     (map class-direct-slots
  333.                          (cdr cpl)))))
  334.             (nfields 0)
  335.             (field-initializers '())
  336.             (allocator
  337.               (lambda (init)
  338.             (let ((f nfields))
  339.               (set! nfields (+ nfields 1))
  340.               (set! field-initializers
  341.                 (cons init field-initializers))
  342.               (list (lambda (o)   (%instance-ref  o f))
  343.                 (lambda (o n) (%instance-set! o f n))))))
  344.             (getters-n-setters
  345.               (map (lambda (s)
  346.                  (cons (car s)
  347.                    (allocator (lambda () '()))))
  348.                slots)))
  349.  
  350.            (slot-set! new 'direct-supers      dsupers)
  351.            (slot-set! new 'direct-slots       dslots)
  352.            (slot-set! new 'cpl                cpl)
  353.            (slot-set! new 'slots              slots)
  354.            (slot-set! new 'nfields            nfields)
  355.            (slot-set! new 'field-initializers (reverse
  356.                            field-initializers))
  357.            (slot-set! new 'getters-n-setters  getters-n-setters)
  358.            new))
  359.         ((eq? class <generic>)
  360.          (let ((new (%allocate-entity class
  361.                       (length (class-slots class)))))
  362.            (slot-set! new 'methods '())
  363.            new))
  364.         ((eq? class <method>)
  365.          (let ((new (%allocate-instance
  366.              class
  367.              (length (class-slots class)))))
  368.            (slot-set! new
  369.               'specializers
  370.               (getl initargs 'specializers))
  371.            (slot-set! new
  372.               'procedure
  373.               (getl initargs 'procedure))
  374.            new)))))
  375.  
  376.  
  377. ;
  378. ; These are the real versions of slot-ref and slot-set!.  Because of the
  379. ; way the new slot access protocol works, with no generic call in line,
  380. ; they can be defined up front like this.  Cool eh?
  381. ;
  382. ;
  383. (define slot-ref
  384.     (lambda (object slot-name)
  385.       (let* ((info   (lookup-slot-info (class-of object) slot-name))
  386.          (getter (list-ref info 0)))
  387.     (getter object))))
  388.  
  389. (define slot-set!
  390.     (lambda (object slot-name new-value)
  391.       (let* ((info   (lookup-slot-info (class-of object) slot-name))
  392.          (setter (list-ref info 1)))
  393.     (setter object new-value))))
  394.  
  395. (define lookup-slot-info
  396.     (lambda (class slot-name)
  397.       (let* ((getters-n-setters
  398.            (if (eq? class <class>)           ;* This grounds out
  399.            getters-n-setters-for-class   ;* the slot-ref tower.
  400.            (slot-ref class 'getters-n-setters)))
  401.          (entry (assq slot-name getters-n-setters)))
  402.     (if entry
  403.         (cdr entry)
  404.         (error "No slot" slot-name "in instances of" class)))))
  405.  
  406.  
  407.  
  408. ;
  409. ; Given that the early version of MAKE is allowed to call accessors on
  410. ; class metaobjects, the definitions for them come here, before the
  411. ; actual class definitions, which are coming up right afterwards.
  412. ;
  413. ;
  414. (define class-direct-slots
  415.     (lambda (class) (slot-ref class 'direct-slots)))
  416. (define class-direct-supers
  417.     (lambda (class) (slot-ref class 'direct-supers)))
  418. (define class-slots
  419.     (lambda (class) (slot-ref class 'slots)))
  420. (define class-cpl
  421.     (lambda (class) (slot-ref class 'cpl)))
  422.  
  423. (define generic-methods
  424.     (lambda (generic) (slot-ref generic 'methods)))
  425.  
  426. (define method-specializers
  427.     (lambda (method) (slot-ref method 'specializers)))
  428. (define method-procedure
  429.     (lambda (method) (slot-ref method 'procedure)))
  430.  
  431.  
  432. ;
  433. ; The next 7 clusters define the 6 initial classes.  It takes 7 to 6
  434. ; because the first and fourth both contribute to <class>.
  435. ;
  436. (define the-slots-of-a-class     ;
  437.     '(direct-supers              ;(class ...)        
  438.       direct-slots               ;((name . options) ...)
  439.       cpl                        ;(class ...) 
  440.       slots                      ;((name . options) ...) 
  441.       nfields                    ;an integer
  442.       field-initializers         ;(proc ...)
  443.       getters-n-setters))        ;((slot-name getter setter) ...)
  444.                                  ;
  445. (define getters-n-setters-for-class      ;see lookup-slot-info
  446.     ;
  447.     ; I know this seems like a silly way to write this.  The
  448.     ; problem is that the obvious way to write it seems to
  449.     ; tickle a bug in MIT Scheme!
  450.     ;
  451.     (let ((make-em (lambda (s f)
  452.              (list s
  453.                (lambda (o)   (%instance-ref  o f))
  454.                (lambda (o n) (%instance-set! o f n))))))
  455.       (map (lambda (s)
  456.          (make-em s (position-of s the-slots-of-a-class)))
  457.        the-slots-of-a-class)))
  458. (define <class> (%allocate-instance #f (length the-slots-of-a-class)))
  459. (%set-instance-class-to-self <class>)
  460.  
  461. (define <top>          (make <class>
  462.                  'direct-supers (list)
  463.                  'direct-slots  (list)))
  464.  
  465. (define <object>       (make <class>
  466.                  'direct-supers (list <top>)
  467.                  'direct-slots  (list)))
  468.  
  469. ;
  470. ; This cluster, together with the first cluster above that defines
  471. ; <class> and sets its class, have the effect of:
  472. ;
  473. ;   (define <class>
  474. ;     (make <class>
  475. ;           'direct-supers (list <object>)
  476. ;           'direct-slots  (list 'direct-supers ...)))
  477. ;
  478. (slot-set! <class> 'direct-supers      (list <object>))
  479. (slot-set! <class> 'direct-slots       (map list the-slots-of-a-class))
  480. (slot-set! <class> 'cpl                (list <class> <object> <top>))
  481. (slot-set! <class> 'slots              (map list the-slots-of-a-class))
  482. (slot-set! <class> 'nfields            (length the-slots-of-a-class))
  483. (slot-set! <class> 'field-initializers (map (lambda (s)
  484.                           (lambda () '()))
  485.                         the-slots-of-a-class))
  486. (slot-set! <class> 'getters-n-setters  '())
  487.  
  488.  
  489. (define <procedure-class> (make <class>
  490.                 'direct-supers (list <class>)
  491.                 'direct-slots  (list)))
  492.  
  493. (define <entity-class>    (make <class>
  494.                     'direct-supers (list <procedure-class>)
  495.                     'direct-slots  (list)))
  496.  
  497. (define <generic>         (make <entity-class>
  498.                     'direct-supers (list <object>)
  499.                     'direct-slots  (list 'methods)))
  500.  
  501. (define <method>          (make <clavi>
  502.                     'direct-supers (list <object>)
  503.                     'direct-slots  (list 'specializers
  504.                              'procedure)))
  505.  
  506.  
  507.  
  508. ;
  509. ; These are the convenient syntax we expose to the base-level user.
  510. ;
  511. ;
  512. (define make-class
  513.     (lambda (direct-supers direct-slots)
  514.       (make <class>
  515.         'direct-supers direct-supers
  516.         'direct-slots  direct-slots)))
  517.  
  518. (define make-generic
  519.     (lambda ()
  520.       (make <generic>)))
  521.  
  522. (define make-method
  523.     (lambda (specializers procedure)
  524.       (make <method>
  525.         'specializers specializers
  526.         'procedure    procedure)))
  527.  
  528.  
  529.  
  530.  
  531. ;
  532. ; The initialization protocol
  533. ;
  534. (define initialize (make-generic))
  535.         
  536.  
  537. ;
  538. ; The instance structure protocol.
  539. ;
  540. (define allocate-instance (make-generic))
  541. (define compute-getter-and-setter (make-generic))
  542.  
  543.  
  544. ;
  545. ; The class initialization protocol.
  546. ;
  547. (define compute-cpl   (make-generic))
  548. (define compute-slots (make-generic))
  549.  
  550. ;
  551. ; The generic invocation protocol.
  552. ;
  553. (define compute-apply-generic         (make-generic))
  554. (define compute-methods               (make-generic))
  555. (define compute-method-more-specific? (make-generic))
  556. (define compute-apply-methods         (make-generic))
  557.  
  558.  
  559.  
  560.  
  561. ;
  562. ; The next thing to do is bootstrap generic functions.
  563. (define generic-invocation-generics (list compute-apply-generic
  564.                       compute-methods
  565.                       compute-method-more-specific?
  566.                       compute-apply-methods))
  567.  
  568. (define add-method
  569.     (lambda (generic method)
  570.       (slot-set! generic
  571.          'methods
  572.          (cons method
  573.                (filter-in
  574.             (lambda (m)
  575.               (not (every eq?
  576.                       (method-specializers m)
  577.                       (method-specializers method))))
  578.             (slot-ref generic 'methods))))
  579.       (%set-instance-proc! generic (compute-apply-generic generic))))
  580.  
  581. ;
  582. ; Adding a method calls COMPUTE-APPLY-GENERIC, the result of which calls
  583. ; the other generics in the generic invocation protocol.  Two, related,
  584. ; problems come up.  A chicken and egg problem and a infinite regress
  585. ; problem.
  586. ;
  587. ; In order to add our first method to COMPUTE-APPLY-GENERIC, we need
  588. ; something sitting there, so it can be called.  The first definition
  589. ; below does that.
  590. ; Then, the second definition solves both the infinite regress and the
  591. ; not having enough of the protocol around to build itself problem the
  592. ; same way: it special cases invocation of generics in the invocation
  593. ; protocol.
  594. ;
  595. ;
  596. (%set-instance-proc! compute-apply-generic
  597.      (lambda (generic)             ;The ONE time this is called
  598.                    ;it doesn't get cnm.
  599.        (lambda args
  600.      (apply (method-procedure (car (generic-methods generic)))
  601.         (cons #f args))))) ;But, the ONE time it is run,
  602.                    ;it needs to pass a dummy
  603.                    ;value for cnm!
  604.  
  605. (add-method compute-apply-generic
  606.     (make-method (list <generic>)
  607.       (lambda (call-next-method generic)
  608.     (lambda args
  609.       (if (and (memq generic generic-invocation-generics)     ;* G  c
  610.            (memq (car args) generic-invocation-generics)) ;* r  a
  611.           (apply (method-procedure                            ;* o  s
  612.               (last (generic-methods generic)))           ;* u  e
  613.              (cons #f args))                              ;* n
  614.                                                               ;* d
  615.           ((compute-apply-methods generic)
  616.            ((compute-methods generic) args)
  617.            args))))))
  618.  
  619.  
  620. (add-method compute-methods
  621.     (make-method (list <generic>)
  622.       (lambda (call-next-method generic)
  623.     (lambda (args)
  624.       (let ((applicable
  625.          (filter-in (lambda (method)
  626.                   ;
  627.                   ; Note that every only goes as far as the
  628.                   ; shortest list!
  629.                   ;
  630.                   (every applicable?
  631.                      (method-specializers method)
  632.                      args))
  633.                 (generic-methods generic))))
  634.         (gsort (lambda (m1 m2)
  635.              ((compute-method-more-specific? generic)
  636.               m1
  637.               m2
  638.               args))
  639.            applicable))))))
  640.  
  641.  
  642. (add-method compute-method-more-specific?
  643.     (make-method (list <generic>)
  644.       (lambda (call-next-method generic)
  645.     (lambda (m1 m2 args)
  646.       (let loop ((specls1 (method-specializers m1))
  647.              (specls2 (method-specializers m2))
  648.              (args args))
  649.         (cond ((null? specls1) (return #t))     ;*Maybe these two
  650.           ((null? specls2) (return #f))     ;*should barf?
  651.           ((null? args)
  652.            (error "Fewer arguments than specializers."))
  653.           (else
  654.            (let ((c1  (car specls1))
  655.              (c2  (car specls2))
  656.              (arg (car args)))
  657.              (if (eq? c1 c2)
  658.              (loop (cdr specls1)
  659.                    (cdr specls2)
  660.                    (cdr args))
  661.              (more-specific? c1 c2 arg))))))))))
  662.  
  663.  
  664. (add-method compute-apply-methods
  665.     (make-method (list <generic>)
  666.       (lambda (call-next-method generic)
  667.     (lambda (methods args)
  668.       (letrec ((one-step
  669.              (lambda (tail)
  670.                (lambda ()
  671.              (if (null? tail)
  672.                  (error "No applicable methods/next methods.")
  673.                  (apply (method-procedure (car tail))
  674.                     (cons (one-step (cdr tail)) args)))))))
  675.         ((one-step methods)))))))
  676.  
  677. (define applicable?
  678.     (lambda (c arg)
  679.       (memq c (class-cpl (class-of arg)))))
  680.  
  681. (define more-specific?
  682.     (lambda (c1 c2 arg)
  683.       (memq c2 (memq c1 (class-cpl (class-of arg))))))
  684.  
  685.  
  686.  
  687. (add-method initialize
  688.     (make-method (list <object>)
  689.       (lambda (call-next-method object initargs) object)))
  690.  
  691. (add-method initialize
  692.     (make-method (list <class>)
  693.       (lambda (call-next-method class initargs)
  694.     (call-next-method)
  695.     (slot-set! class
  696.            'direct-supers
  697.            (getl initargs 'direct-supers '()))
  698.     (slot-set! class
  699.            'direct-slots
  700.            (map (lambda (s)
  701.               (if (pair? s) s (list s)))
  702.             (getl initargs 'direct-slots  '())))
  703.     (slot-set! class 'cpl   (compute-cpl   class))
  704.     (slot-set! class 'slots (compute-slots class))
  705.     (let* ((nfields 0)
  706.            (field-initializers '())
  707.            (allocator
  708.         (lambda (init)
  709.           (let ((f nfields))
  710.             (set! nfields (+ nfields 1))
  711.             (set! field-initializers
  712.               (cons init field-initializers))
  713.             (list (lambda (o)   (%instance-ref  o f))
  714.               (lambda (o n) (%instance-set! o f n))))))
  715.            (getters-n-setters
  716.         (map (lambda (slot)
  717.                (cons (car slot)
  718.                  (compute-getter-and-setter class
  719.                             slot
  720.                             allocator)))
  721.              (slot-ref class 'slots))))
  722.       (slot-set! class 'nfields nfields)
  723.       (slot-set! class 'field-initializers field-initializers)
  724.       (slot-set! class 'getters-n-setters getters-n-setters)))))
  725.  
  726. (add-method initialize
  727.     (make-method (list <generic>)
  728.       (lambda (call-next-method generic initargs)
  729.     (call-next-method)
  730.     (slot-set! generic 'methods '())
  731.     (%set-instance-proc! generic
  732.                (lambda args (error "Has no methods."))))))
  733.  
  734. (add-method initialize
  735.     (make-method (list <method>)
  736.       (lambda (call-next-method method initargs)
  737.     (call-next-method)
  738.     (slot-set! method 'specializers (getl initargs 'specializers))
  739.     (slot-set! method 'procedure    (getl initargs 'procedure)))))
  740.  
  741.  
  742.  
  743. (add-method allocate-instance
  744.     (make-method (list <class>)
  745.       (lambda (call-next-method class)
  746.     (let* ((field-initializers (slot-ref class 'field-initializers))
  747.            (new (%allocate-instance
  748.               class
  749.               (length field-initializers))))
  750.       (let loop ((n 0)
  751.              (inits field-initializers))
  752.         (if (pair? inits)
  753.         (begin
  754.          (%instance-set! new n ((car inits)))
  755.          (loop (+ n 1)
  756.                (cdr inits)))
  757.         new))))))
  758.  
  759. (add-method allocate-instance
  760.     (make-method (list <entity-class>)
  761.       (lambda (call-next-method class)
  762.     (let* ((field-initializers (slot-ref class 'field-initializers))
  763.            (new (%allocate-entity
  764.               class
  765.               (length field-initializers))))
  766.       (let loop ((n 0)
  767.              (inits field-initializers))
  768.         (if (pair? inits)
  769.         (begin
  770.          (%instance-set! new n ((car inits)))
  771.          (loop (+ n 1)
  772.                (cdr inits)))
  773.         new))))))
  774.  
  775.  
  776. (add-method compute-cpl
  777.     (make-method (list <class>)
  778.       (lambda (call-next-method class)
  779.     (compute-std-cpl class class-direct-supers))))
  780.  
  781.  
  782. (add-method compute-slots
  783.     (make-method (list <class>)
  784.       (lambda (call-next-method class)
  785.     (let collect ((to-process (apply append
  786.                      (map class-direct-slots
  787.                           (class-cpl class))))
  788.               (result '()))
  789.       (if (null? to-process)
  790.           (reverse result)
  791.           (let* ((current (car to-process))
  792.              (name (car current))
  793.              (others '())
  794.              (remaining-to-process
  795.               (collect-if (lambda (o)
  796.                     (if (eq? (car o) name)
  797.                     (begin
  798.                      (set! others (cons o others))
  799.                      #f)
  800.                     #t))
  801.                   (cdr to-process))))
  802.         (collect remaining-to-process
  803.              (cons (append current
  804.                        (apply append (map cdr others)))
  805.                    result))))))))
  806.  
  807.  
  808. (add-method compute-getter-and-setter
  809.     (make-method (list <class>)
  810.       (lambda (call-next-method class slot allocator)
  811.     (allocator (lambda () '())))))
  812.  
  813.  
  814.  
  815. ;
  816. ; Now everything works, both generic functions and classes, so we can
  817. ; turn on the real MAKE.
  818. ;
  819. ;
  820. (set! make
  821.       (lambda (class . initargs)
  822.     (let ((instance (allocate-instance class)))
  823.       (initialize instance initargs)
  824.       instance)))
  825.  
  826. ;
  827. ; Now define what CLOS calls `built in' classes.
  828. ;
  829. ;
  830. (define <primitive-class>
  831.     (make <class>
  832.       'direct-supers (list <class>)
  833.       'direct-slots  (list)))
  834.  
  835. (define make-primitive-class
  836.     (lambda class
  837.       (make (if (null? class) <primitive-class> (car class))
  838.         'direct-supers (list <top>)
  839.         'direct-slots  (list))))
  840.  
  841. (define <boolean>   (make-primitive-class))
  842. (define <symbol>    (make-primitive-class))
  843. (define <char>      (make-primitive-class))
  844. (define <vector>    (make-primitive-class))
  845. (define <pair>      (make-primitive-class))
  846. (define <number>    (make-primitive-class))
  847. (define <string>    (make-primitive-class))
  848. (define <procedure> (make-primitive-class <procedure-class>))
  849.  
  850.  
  851. ;
  852. ; All done.
  853. ;
  854. ;
  855.  
  856. 'tiny-clos-up-and-running
  857.